package com.zero.ava.service;

import java.util.Properties;

import org.slf4j.Logger;

import com.zero.ava.impl.kernel.ComponentRegistry;
import com.zero.ava.impl.kernel.StandardProperties;

/**
 * 抽象系统服务
 * 
 * @author mac
 * 
 */
public abstract class AbstractService implements IBaseService {

	protected static enum State {
		INITIALIZED, READY, SHUTTING_DOWN, SHUTDOWN
	}

	protected final String appName;

	protected final Logger logger;

	private final Object lock = new Object();

	private State state;

	private int callsInProgress = 0;

	private volatile Thread shutdownThread;

	protected AbstractService(Properties properties, ComponentRegistry systemRegistry, Logger logger) {
		if (properties == null) {
			throw new NullPointerException("null properties");
		} else if (systemRegistry == null) {
			throw new NullPointerException("null systemRegistry");
		} else if (logger == null) {
			throw new NullPointerException("null logger");
		}

		appName = properties.getProperty(StandardProperties.APP_NAME);
		if (appName == null) {
			throw new IllegalArgumentException("The " + StandardProperties.APP_NAME + " property must be specified");
		}

		this.logger = logger;

		setState(State.INITIALIZED);
	}

	public String getName() {
		return toString();
	}

	@Override
	public void ready() throws Exception {
		logger.info(getName() + " ready");
		synchronized (lock) {
			switch (state) {
			case INITIALIZED:
				setState(State.READY);
				break;
			case READY:
				return;
			case SHUTTING_DOWN:
			case SHUTDOWN:
				throw new IllegalStateException("service shutting down");
			default:
				throw new AssertionError();
			}
		}
		doReady();
	}

	protected abstract void doReady() throws Exception;

	@Override
	public void shutDown() {
		logger.info(getName() + "shutdown");

		synchronized (lock) {
			switch (state) {

			case INITIALIZED:
			case READY:
				logger.info(getName() + "initiating shutdown");
				setState(State.SHUTTING_DOWN);
				while (callsInProgress > 0) {
					try {
						lock.wait();
					} catch (InterruptedException e) {
						return;
					}
				}
				shutdownThread = new ShutdownThread();
				shutdownThread.start();
				break;

			case SHUTTING_DOWN:
				break;

			case SHUTDOWN:
				return;

			default:
				throw new AssertionError();
			}
		}

		try {
			shutdownThread.join();
		} catch (InterruptedException e) {
			return;
		}
	}

	protected abstract void doShutdown();

	protected State getState() {
		synchronized (lock) {
			return state;
		}
	}

	protected void callStarted() {
		synchronized (lock) {
			if (shuttingDown()) {
				throw new IllegalStateException("service is shutting down");
			}
			callsInProgress++;
		}
	}

	protected void callFinished() {
		synchronized (lock) {
			callsInProgress--;
			if (state == State.SHUTTING_DOWN && callsInProgress == 0) {
				lock.notifyAll();
			}
		}
	}

	public boolean shuttingDown() {
		synchronized (lock) {
			return state == State.SHUTTING_DOWN || state == State.SHUTDOWN;
		}
	}

	protected boolean isInInitializedState() {
		synchronized (lock) {
			return state == State.INITIALIZED;
		}
	}

	private void setState(State newState) {
		synchronized (lock) {
			state = newState;
		}
	}

	private final class ShutdownThread extends Thread {

		ShutdownThread() {
			super(ShutdownThread.class.getName());
			setDaemon(true);
		}

		public void run() {
			try {
				doShutdown();
			} catch (RuntimeException e) {
				logger.warn("shutting down service throws");
			}
			setState(AbstractService.State.SHUTDOWN);
		}
	}
}
